
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Gateway 结合 Spring Security和 OAuth2 实现分布式认证鉴权 - 学习卡片</title>
      <style>
        body { font-family: sans-serif; background-color: #f0f8ff; color: #333; display: flex; flex-direction: column; align-items: center; padding: 50px 20px; }
        .header h1 { font-size: 32px; }
        .grid-container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; width: 100%; max-width: 1200px; }
        .card-container { perspective: 1200px; cursor: pointer; height: 250px; }
        .card { width: 100%; height: 100%; position: relative; transform-style: preserve-3d; transition: transform 0.7s; border-radius: 16px; box-shadow: 0 4px 16px rgba(0,0,0,0.08); }
        .card-container.flipped .card { transform: rotateY(180deg); }
        .card-face { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; display: flex; flex-direction: column; box-sizing: border-box; border-radius: 16px; background-color: #fff; padding: 24px; }
        .card-back { background-color: #f0fff4; transform: rotateY(180deg); justify-content: space-between; }
        .card-category { font-size: 14px; color: #0052d9; margin-bottom: 8px; font-weight: 500; }
        .card-question { font-size: 20px; font-weight: 500; flex-grow: 1; display: flex; align-items: center; justify-content: center; text-align: center; }
        .card-answer-wrapper { flex-grow: 1; overflow-y: auto; }
        .card-answer { font-size: 15px; line-height: 1.7; }
        .card-footer { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 16px; margin-top: 16px; }
        .card-source { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 12px; margin-top: 12px; }
      </style>
    </head>
    <body>
      <div class="header">
        <h1>Gateway 结合 Spring Security和 OAuth2 实现分布式认证鉴权 - 学习卡片</h1>
      </div>
      <div class="grid-container">
        
    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">架构</div>
          <div class="card-question">在本文描述的分布式系统中，整体架构和核心数据流是怎样的？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">架构</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">系统由网关服务、认证服务和资源服务（如课程、用户服务）组成。数据流为：客户端通过网关登录，从认证服务获取JWT令牌；随后，客户端携带令牌访问资源，网关验证令牌后将请求路由到相应的资源服务。其架构可描述为 `客户端 -> 网关 -> 认证服务/资源服务`。</div>
          </div>
          <div class="card-source">来源: 系统架构设计</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">Spring Cloud Gateway, Spring Security 6, 和 OAuth2 在该系统架构中各自扮演什么核心角色？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">Spring Cloud Gateway 作为 API 网关，是系统的统一入口，负责请求路由、过滤和令牌验证。Spring Security 6 提供强大的 OAuth2 集成，用于保护资源服务并实现细粒度的权限控制。OAuth2 是一种授权协议，为系统提供了标准化的令牌管理机制，定义了 Access Token 和 Refresh Token 的用途。</div>
          </div>
          <div class="card-source">来源: 核心概念</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">配置</div>
          <div class="card-question">在配置 OAuth2 授权服务器时，`RegisteredClientRepository` 的作用是什么？文档中的示例客户端支持哪些授权模式？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">配置</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">`RegisteredClientRepository` 用于定义和注册客户端应用程序。在文档的示例中，它注册了一个名为 `edu-client` 的客户端，并为其配置了 `CLIENT_SECRET_BASIC` 认证方法，同时启用了 `密码模式 (AuthorizationGrantType.PASSWORD)` 和 `刷新令牌模式 (AuthorizationGrantType.REFRESH_TOKEN)`。</div>
          </div>
          <div class="card-source">来源: 实战案例 在线教育平台 > 步骤 1：配置 OAuth2 授权服务器（auth-service）</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">Spring Cloud Gateway 是如何验证 JWT 并将认证信息传递给下游服务的？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">网关通过 `oauth2ResourceServer().jwt()` 配置来启用对 JWT 令牌的验证。对于需要认证的路径（如 `/courses/**`），它会校验令牌的有效性。同时，通过 `TokenRelay` 过滤器，网关会将客户端请求头中的 JWT 令牌原封不动地传递给下游的资源服务，从而使下游服务也能获取用户信息和权限。</div>
          </div>
          <div class="card-source">来源: 实战案例 在线教育平台 > 步骤 2：Spring Cloud Gateway 配置（gateway-service）</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">特性</div>
          <div class="card-question">资源服务器（course-service）如何实现基于 JWT 中 'scope' 的细粒度权限控制？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">特性</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">资源服务器通过自定义的 `JwtAuthenticationConverter` 和 `CustomRoleConverter` 将 JWT 中的 `scope` 声明（如 `course:read`）转换成 Spring Security 可识别的权限格式（如 `SCOPE_course:read`）。随后，在 Controller 的方法上使用 `@PreAuthorize("hasAuthority('SCOPE_course:read')")` 注解来精确控制只有具备相应权限的用户才能访问该接口。</div>
          </div>
          <div class="card-source">来源: 实战案例 在线教育平台 > 步骤 3：资源服务器（课程服务）配置（course-service）</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">实战</div>
          <div class="card-question">如何使用 curl 命令通过密码模式从授权服务器获取 Access Token？请说明关键参数。</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">实战</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">可以使用以下命令：`curl -X POST http://localhost:8081/oauth2/token -H "Authorization: Basic ZWR1LWNsaWVudDpzZWNyZXQ=" -d "grant_type=password&username=student1&password=123456&scope=course:read"`。关键参数包括：`-H "Authorization: Basic ..."` 包含了 Base64 编码的客户端ID和密钥；`-d` 中的 `grant_type=password` 指定了授权模式，并提供了用户的 `username`、`password` 和请求的 `scope`。</div>
          </div>
          <div class="card-source">来源: 实战案例 在线教育平台 > 步骤 1：配置 OAuth2 授权服务器（auth-service）</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">该项目的核心技术栈选型是什么？包括 Spring 版本、Java 版本以及数据存储方案。</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">该项目的技术栈包括：Spring Boot 3.x 作为基础框架，Spring Cloud 2022.x 用于网关和服务发现，Spring Security 6.x 负责安全控制。运行时环境为 Java 17。数据存储方面，使用 MySQL 存储用户信息等业务数据，并使用 Redis 进行令牌存储。</div>
          </div>
          <div class="card-source">来源: 技术选型与环境准备 > 技术栈</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">在授权服务器的配置中，`JWKSource` Bean 的作用是什么？它是如何保证 JWT 安全性的？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">`JWKSource` Bean 的作用是提供用于签名 JWT 的加密密钥。在文档的实现中，它通过 `generateRsaKey()` 方法生成一个 2048 位的 RSA 密钥对。然后，使用这个密钥对通过 `NimbusJwtEncoder` 对 JWT进行签名。这种非对称加密签名机制确保了 JWT 的内容没有被篡改，并且是由可信的授权服务器颁发的，从而保证了其安全性。</div>
          </div>
          <div class="card-source">来源: 实战案例 在线教育平台 > 步骤 1：配置 OAuth2 授权服务器（auth-service）</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">实战</div>
          <div class="card-question">当 Access Token 过期后，客户端应如何使用 Refresh Token 来获取新的令牌？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">实战</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">客户端可以向令牌端点发起一个 POST 请求，并将 `grant_type` 设置为 `refresh_token`，同时在请求体中附上之前获得的 `refresh_token`。例如：`curl -X POST http://localhost:8080/oauth2/token -H "Authorization: Basic ZWR1LWNsaWVudDpzZWNyZXQ=" -d "grant_type=refresh_token&refresh_token=xyz..."`。授权服务器验证通过后，会返回一个新的 Access Token 和一个新的 Refresh Token。</div>
          </div>
          <div class="card-source">来源: 实战案例 在线教育平台 > 步骤 6：Redis 存储与令牌刷新</div>
        </div>
      </div>
    </div>

      </div>
    </body>
    </html>
